<?php
require_once 'OpModel.php';

/**
 * 数据库模型，封装了对数据库的访问接口
 * @author wangjiajun
 *
 */
class OpDbModel extends OpModel
{
    private static $_instances;
    
    private $_db;
    
    /**
     * 构造函数
     * @param array $dbConfig 
     * 参数格式如下
     * array(
     * 	'host' => ... 地址
     * 	'port' => ... 端口，可选，默认为3306
     * 	'user' => ... 用户名
     * 	'pass' => ... 密码
     * 	'name' => ... 数据库名
     * )
     * @param Log $logger PEAR::Log对象
     */
    function __construct($dbConfig, $logger = null)
    {
        parent::__construct($logger);
        
        $host = $dbConfig['host'];
        $port = isset($dbConfig['port']) ? $dbConfig['port'] : 3306;
        $name = $dbConfig['name'];
        $user = $dbConfig['user'];
        $pass = $dbConfig['pass'];
        $this->_db = new PDO("mysql:host=$host;port=$port;dbname=$name", $user, $pass, array(
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
            PDO::ATTR_PERSISTENT => false
        ));
    }
    
    function __destruct()
    {
        $this->_db = null;
    }
    
    /**
     * 获取对象，对于完全相同的连接参数，共享同一个实例
     * @param array $dbConfig
     * @param Log $logger
     * @return OpDbModel
     */
    public static function getInstance($dbConfig, $logger = null)
    {
        $host = $dbConfig['host'];
        $port = isset($dbConfig['port']) ? $dbConfig['port'] : 3306;
        $name = $dbConfig['name'];
        $user = $dbConfig['user'];
        $pass = $dbConfig['pass'];
        $key = md5($host . $port . $name . $user . $pass);
        if (!isset(self::$_instances[$key])) {
            self::$_instances[$key] = new self($dbConfig, $logger);
        }
        return self::$_instances[$key];
    }
    
    /**
     * 设置PDO对象属性
     * @param string $attribute
     * @param mixed $value
     * @return boolean
     */
    public function setAttribute($attribute, $value)
    {
        return $this->_db->setAttribute($attribute, $value);
    }
    
    /**
     * 执行查询语句，返回全部结果
     * @param string $sql
     * @param int $fetchStyle
     * @return array|boolean
     */
    public function query($sql, $fetchStyle = PDO::FETCH_ASSOC)
    {
        $this->_logger->debug($sql);
        $st = $this->_db->query($sql);
        if ($st === false) {
            $errorInfo = $this->_db->errorInfo();
            $this->_logger->err("db query failed: errno-{$errorInfo[1]}, error-{$errorInfo[2]}, sql-$sql");
            return false;
        }
        return $st->fetchAll($fetchStyle);
    }
    
    /**
     * 执行查询语句，返回第一行，如果没有结果，返回null
     * @param string $sql
     * @param int $fetchStyle
     * @return array|null|boolean
     */
    public function queryRow($sql, $fetchStyle = PDO::FETCH_ASSOC)
    {
        $this->_logger->debug($sql);
        $st = $this->_db->query($sql);
        if ($st === false) {
            $errorInfo = $this->_db->errorInfo();
            $this->_logger->err("db query failed: errno-{$errorInfo[1]}, error-{$errorInfo[2]}, sql-$sql");
            return false;
        }
        $row = $st->fetch($fetchStyle);
        $st->closeCursor();
        if (is_array($row)) {
            return $row;
        } else {
            return null;
        }
    }
    
    /**
     * 执行查询语句，返回第一行第一列，如果没有结果，返回null
     * @param string $sql
     * @return string|boolean
     */
    public function queryOne($sql)
    {
        $this->_logger->debug($sql);
        $st = $this->_db->query($sql);
        if ($st === false) {
            $errorInfo = $this->_db->errorInfo();
            $this->_logger->err("db query failed: errno-{$errorInfo[1]}, error-{$errorInfo[2]}, sql-$sql");
            return false;
        }
        $row = $st->fetch(PDO::FETCH_NUM);
        $st->closeCursor();
        if (is_array($row)) {
            return $row[0];
        } else {
            return null;
        }
    }
    
    /**
     * 执行修改语句
     * @param string $sql
     * @param boolean $returnAffectedRows
     * @return boolean|int
     */
    public function exec($sql, $returnAffectedRows = false)
    {
        $this->_logger->debug($sql, OpModel::LOG_DEBUG);
        $result = $this->_db->exec($sql);
        if ($result === false) {
            $errorInfo = $this->_db->errorInfo();
            $this->_logger->err("db exec failed: errno-{$errorInfo[1]}, error-{$errorInfo[2]}, sql-$sql");
            return false;
        } else {
            return $returnAffectedRows ? $result : true;
        }
    }
    
    /**
     * 查询最后一次插入ID
     */
    public function getLastInsertId()
    {
        return $this->_db->lastInsertId();
    }
    
    /**
     * prepare SQL语句
     * @param string $sql
     * @param array $options
     * @return PDOStatement|boolean
     */
    public function prepare($sql, $options)
    {
        return $this->_db->prepare($sql, $options);
    }
    
    /**
     * 返回一个PDO数据库对象，方便特殊调用
     * @return PDO
     */
    public function getPDO() {
        return $this->_db;
    }
}
